using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace minega
{
    /// <summary>
    /// Class holding information of the cells (if they are mines or not).
    /// </summary>
    public class Mines
    {
        /// <summary>
        /// Byte-array storing whether a cell has a mine
        /// 1 bit per cell = 8 cells per byte
        /// </summary>
        private byte[] _raw;
        /// <summary>
        /// Width and Height of the grid and the number of mines.
        /// </summary>
        private int _width, _height, _count;
        /// <summary>
        /// Gets the width of the grid.
        /// </summary>
        public int Width
        {
            get { return _width; }
        }
        /// <summary>
        /// Gets the height of the grid.
        /// </summary>
        public int Height
        {
            get { return _height; }
        }
        /// <summary>
        /// Gets the number of mines in the grid.
        /// </summary>
        public int Count
        {
            get { return _count; }
        }
        /// <summary>
        /// Checks if the cell at (x,y) is a mine or not.
        /// </summary>
        /// <param name="x">x-coordinate.</param>
        /// <param name="y">y-coordinate.</param>
        /// <returns>True if the cell's a mine, otherwise false.</returns>
        public bool this[int x, int y]
        {
            get 
            {
                if (x > _width)
                    throw new IndexOutOfRangeException();

                int idx = x + y * _width;
                int rawIdx = idx / 8;
                int rawBit = idx % 8;
                return (_raw[rawIdx] & (1 << rawBit)) != 0;
            }
            private set
            {
                if (x > _width)
                    throw new IndexOutOfRangeException();

                int idx = x + y * _width;
                int rawIdx = idx / 8;
                int rawBit = idx % 8;
                if (value)
                    _raw[rawIdx] |= (byte)(1 << rawBit);
                else _raw[rawIdx] &= (byte)(~(1 << rawBit));
            }
        }
        /// <summary>
        /// Initializes everything to zero.
        /// </summary>
        public Mines()
            : this(0, 0, 0)
        {
            
        }
        /// <summary>
        /// Sets the width, height and amount of mines.
        /// </summary>
        /// <param name="w">Width of the grid.</param>
        /// <param name="h">Height of the grid.</param>
        /// <param name="mines">Amount of mines in the grid.</param>
        public Mines(int w, int h, int mines)
        {
            _width = w;
            _height = h;
            _count = mines;

            // + 7 in the denominator to make sure we don't miss any.
            _raw = new byte[(_width * _height + 7) / 8];
        }
        /// <summary>
        /// Generates a new grid, i.e randomly choses positions to the specified amount of mines in the grid.
        /// </summary>
        public void GenerateNew()
        {
			for (int i = 0; i < _raw.Length; i++)
				_raw[i] = 0;

            int mines = 0;
            Random r = new Random();
            while (mines < _count)
            {
                int x = r.Next(_width);
                int y = r.Next(_height);
                if (!this[x, y])
                {
                    this[x, y] = true;
                    mines++;
                }
            }
        }
        /// <summary>
        /// Loads the mines from a binary string representation. 
        /// You have to set the Width and Height properties first
        /// </summary>
        /// <param name="str">The binary string representation to parse</param>
        public void LoadFromBinaryStringRepresentation(string str)
        {
            if (str.Length != _width * _height)
                throw new ArgumentException("Length of string doesn't match width * height");

            _count = 0;
            for (int i = 0; i < _raw.Length; i++)
                _raw[i] = 0;
            for (int x = 0; x < _width; x++)
            {
                for (int y = 0; y < _height; y++)
                {
                    if (str[x + y * _width] == '1')
                    {
                        this[x, y] = true;
                        _count++;
                    }
                }
            }
        }
        /// <summary>
        /// Calculates the number of neighbouring mines.
        /// </summary>
        /// <param name="x">x-coordinate of the cell.</param>
        /// <param name="y">y-coordinate of the cell.</param>
        /// <returns>Number of neighbouring mines.</returns>
        public byte NeightbouringMines(int x, int y)
        {
            byte mines = 0;
			Rectangle rect = new Rectangle(0, 0, _width, _height);
			for (byte b = Neighbour.UL; b <= Neighbour.DR; b++)
			{
				Point pt = NeighbourPos.GetNeighbour(b, x, y);
				if (rect.Contains(pt))
				{
					if (this[pt.X, pt.Y])
						mines++;
				}
			}
            return mines;
        }
    }
}